﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using System.IO;
using System.Xml.Serialization;

namespace TV_Organizer
{
    public static class ExtensionMethods
    {
        #region

        public static int[] IndexesOf(this string source, char character)
        {
            var l_result = new Collection<int>();
            for (int i = 0; i < source.Length; i++)
            {
                if (source[i] == character)
                    l_result.Add(i);
            }
            return l_result.ToArray();
        }

        #endregion

        #region DependencyObject

        /// <summary>
        /// Use this to get the value of a DependencyProperty when you don't know if you are in a Dispatcher thread.
        /// For example, it can be called in the getter of the corresponding Property.
        /// Inspired from : http://khason.net/blog/dependency-property-getters-and-setters-in-multithreaded-environment/
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dpObject"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static T SafelyGetValue<T>(this DependencyObject dpObject, DependencyProperty property)
        {
            if (!typeof(T).IsAssignableFrom(property.PropertyType))
                throw new InvalidOperationException();

            if (Thread.CurrentThread == dpObject.Dispatcher.Thread)
            {
                return (T)dpObject.GetValue(property);
            }
            else
            {
                try
                {
                    return (T)dpObject.Dispatcher.Invoke(
                        System.Windows.Threading.DispatcherPriority.Background,
                        (DispatcherOperationCallback)delegate { return dpObject.GetValue(property); },
                        property);
                }
                catch
                {
                    return (T)property.DefaultMetadata.DefaultValue;
                }
            }
        }

        /// <summary>
        /// Use this to set the value of a DependencyProperty when you don't know if you are in a Dispatcher thread.
        /// For example, it can be called in the setter of the corresponding Property.
        /// Inspired from : http://khason.net/blog/dependency-property-getters-and-setters-in-multithreaded-environment/
        /// </summary>
        /// <param name="dpObject"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        public static void SafelySetValue(this DependencyObject dpObject, DependencyProperty property, object value)
        {
            if (Thread.CurrentThread == dpObject.Dispatcher.Thread)
            {
                dpObject.SetValue(property, value);
            }
            else
            {
                //dpObject.Dispatcher.Invoke(new System.Action<DependencyObject, DependencyProperty, object>((dpo, dp, o) =>
                //{
                //    dpo.SetValue(dp, o);
                //}), new object[] { dpObject, property, value });
                dpObject.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Background,
                    (SendOrPostCallback)delegate { dpObject.SetValue(property, value); },
                    value);
            }
        }

        public static T GetDataContext<T>(this object obj)
        {
            object result = null;

            if (obj is FrameworkContentElement)
                result = ((FrameworkContentElement)obj).DataContext;
            if (obj is FrameworkElement)
                result = ((FrameworkElement)obj).DataContext;

            if (result is T)
                return (T)result;
            else
                return default(T);
        }

        #endregion

        #region ICollection

        public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> inputs)
        {
            foreach (var input in inputs)
                collection.Add(input);
        }

        #endregion

        #region string

        public static string RemoveChars(this string source, char[] oldChars)
        {
            var l_result = new Collection<char>();
            foreach (var c in source.ToCharArray().Where(cc => !oldChars.Contains(cc)))
                l_result.Add(c);

            return new String(l_result.ToArray());
        }

        public static string ReplaceChars(this string source, char[] oldChars, char newChar)
        {
            var l_result = source.ToCharArray();
            for (int i = 0; i < l_result.Length; i++)
            {
                if (oldChars.Contains(l_result[i]))
                    l_result[i] = newChar;
            }

            return new String(l_result.ToArray());
        }

        #endregion

        #region XML String <-> Object

        public static T DeserializeXML<T>(this string serializedXML)
        {
            var l_stringReader = new StringReader(serializedXML);

            XmlSerializer serializer = null;
            try
            {
                serializer = new XmlSerializer(typeof(T));
            }
            catch (Exception e)
            {
                MessageBox.Show(string.Format("Could not create a XmlSerializer for type {0} because of a {1}:{2}{3}", typeof(T).Name,
                    e.GetBaseException().GetType().Name, Environment.NewLine, e.GetBaseException().Message));
                return default(T);
            }

            object result = null;
            try
            {
                result = serializer.Deserialize(l_stringReader);
            }
            catch (Exception e)
            {
                MessageBox.Show(string.Format("Could not deserialize an object of type {0} because of a {1}:{2}{3}", typeof(T).Name,
                    e.GetBaseException().GetType().Name, Environment.NewLine, e.GetBaseException().Message));
                return default(T);
            }

            l_stringReader.Close();

            return (T)result;
        }

        /// <summary>
        /// Tries to serialize an object to Xml. If the serialization fails, returns null
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeToXML(this object obj)
        {
            XmlSerializer serializer = null;
            try
            {
                serializer = new XmlSerializer(obj.GetType());
            }
            catch (Exception e)
            {
                MessageBox.Show(string.Format("Could not create a XmlSerializer for type {0} because of a {1}:{2}{3}", obj.GetType().Name,
                    e.GetBaseException().GetType().Name, Environment.NewLine, e.GetBaseException().Message));
                return string.Empty;
            }

            var l_stringWriter = new StringWriter();

            try
            {
                serializer.Serialize(l_stringWriter, obj);
            }
            catch (Exception e)
            {
                MessageBox.Show(string.Format("Could not serialize an object of type {0} because of a {1}:{2}{3}", obj.GetType().Name,
                    e.GetBaseException().GetType().Name, Environment.NewLine, e.GetBaseException().Message));
                return string.Empty;
            }

            var result = l_stringWriter.ToString();
            l_stringWriter.Close();
            return result;
        }

        #endregion
    }
}
